package rad.framework.jsf;

import java.lang.reflect.Method;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import rad.framework.lang.ReflectionUtil;

import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.NaturalId;
import org.jboss.seam.Component;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.faces.Converter;
import org.jboss.seam.annotations.intercept.BypassInterceptors;

@Name("rad.framework.jsf.NaturalIdConverter")
@BypassInterceptors
@Converter
public class NaturalIdConverter implements javax.faces.convert.Converter {

	@Transactional
	public Object getAsObject(FacesContext context, UIComponent cmp,
			String value) {

		if (StringUtils.isEmpty(value)) {
			return null;
		}

		try {
			EntityManager entityManager = getEntityManager();
			entityManager.joinTransaction();

			String entityName = getEntityName(cmp);
			String propertyName = getPropertyName(value);

			Query qry = entityManager.createQuery("from " + entityName
					+ " where " + propertyName + " = :" + propertyName + "");
			qry.setParameter(propertyName, value);

			return qry.getSingleResult();
		} catch (NoResultException e) {
			return value;
		}
	}

	public String getAsString(FacesContext context, UIComponent cmp,
			Object value) {
		if (value == null) {
			return null;
		}
		if (value instanceof String) {
			return (String) value;
		}
		try {
			return getNaturalId(value);
		} catch (Exception e) {
			throw new ConverterException(e);
		}
	}

	protected EntityManager getEntityManager() {
		return (EntityManager) Component.getInstance("entityManager");
	}

	protected String getEntityName(UIComponent cmp) {
		return cmp.getValueExpression("value").getExpectedType()
				.getSimpleName();
	}

	protected String getPropertyName(Object value) {
		List<Method> methods = ReflectionUtil.findMethodsByAnnotation(value
				.getClass(), NaturalId.class);
		for (Method method : methods) {
			String name = method.getName();
			name = name.substring(3);
			return StringUtils.uncapitalize(name);
		}
		return value.toString();
	}

	protected String getNaturalId(Object value) {
		List<Method> methods = ReflectionUtil.findMethodsByAnnotation(value
				.getClass(), NaturalId.class);
		for (Method method : methods) {
			return ReflectionUtil.invoke(method, value, new Object[] {})
					.toString();
		}
		return value.toString();
	}
}
